﻿using AutoMapper;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using YaAppoint.Appointments.Pools;
using YaAppoint.Common;
using YaAppoint.CustomSettings;
using YaAppoint.Devices;
using YaAppoint.ExamApplications;
using YaAppoint.OnlineServiceCache;
using YaAppoint.Response;
using YaAppoint.TextTemplates;
using YaAppoint.Users;

namespace YaAppoint.Appointments.ManualAppoints
{
    /// <summary>
    /// 手动预约服务
    /// </summary>
    [RemoteService(isEnabled: false)]
    public class ManualAppointAppService(IRepository<ApplyDepartment, Guid> applyDepRepository,
                                         ICurrentUserDepartmentAppService currentUserDep,
                                         IRepository<Department, Guid> depparmentRepository,
                                         IConfiguration configuration,
                                         IAppointmentAppService appointmentAppService,
                                         ICommonAppService commonAppService,
                                         IRepository<Pool, Guid> poolRepository,
                                         IRepository<PlanPool, Guid> planPoolRepository,
                                         IRepository<Appointment, Guid> appointmentRepository,
                                         IRepository<Device, Guid> deviceRepository,
                                         IRepository<ExamApplication, Guid> examApplicationRepository,
                                         AsyncQueryableExecuter queryableExecuter,
                                         IExamApplyAppService examApplyAppService,
                                         IPoolAppService poolAppService,
                                         ICustomSettingAppService customSettingAppService,
                                         IOnlineServiceCacheAppService onlineServiceCacheAppService,
                                         IRepository<TextTemplate, Guid> textTemplateRepository,
                                         IMapper mapper) : ApplicationService, IManualAppointAppService, ITransientDependency
    {
        private readonly IMapper _mapper = mapper;
        private readonly IRepository<ApplyDepartment, Guid> _applyDepRepository = applyDepRepository;
        private readonly IRepository<ExamApplication, Guid> _examApplicationRepository = examApplicationRepository;
        private readonly ICommonAppService _commonAppService = commonAppService;
        private readonly IExamApplyAppService _examApplyAppService = examApplyAppService;
        private readonly IPoolAppService _poolAppService = poolAppService;
        private readonly ICurrentUserDepartmentAppService _currentUserDep = currentUserDep;
        private readonly IRepository<TextTemplate, Guid> _textTemplateRepository = textTemplateRepository;
        private readonly IConfiguration _configuration = configuration;

        private readonly IRepository<Department, Guid> _depparmentRepository = depparmentRepository;
        private readonly IRepository<Pool, Guid> _poolRepository = poolRepository;
        private readonly IRepository<PlanPool, Guid> _planPoolRepository = planPoolRepository;
        private readonly IRepository<Device, Guid> _deviceRepository = deviceRepository;
        private readonly IRepository<Appointment, Guid> _appointmentRepository = appointmentRepository;
        private readonly AsyncQueryableExecuter _queryableExecuter = queryableExecuter;
        private readonly IAppointmentAppService _appointmentAppService = appointmentAppService;
        private readonly ICustomSettingAppService _customSettingAppService = customSettingAppService;
        private readonly IOnlineServiceCacheAppService _onlineServiceCacheAppService = onlineServiceCacheAppService;


        public async Task<BackJson> GetDateLimit(Guid devicId)
        {
            var setting = await _customSettingAppService.GetDeviceSetting(devicId);
            return new BackJson
            {
                code = 0,
                data = setting.AppointableCount!
            };
        }

        /// <summary>
        /// 获取申请科室选项
        /// </summary>
        public async Task<BackJson> GetApplyDepOptions()
        {
            var insCode= _configuration["InstitutionCode"];
            List<ApplyDepartment> list = null;
            if (insCode != null)
            {
                list = await _applyDepRepository.GetListAsync(x => x.IsActive && x.InstitutionCode == insCode!);
            }
            else
            { 
                list = await _applyDepRepository.GetListAsync(x => x.IsActive);
            }
            return new BackJson()
            {
                code = 0,
                data = list.Select(x => new { label = x.Name, value = x.Id })
            };
        }

        /// <summary>
        /// 根据选中申请项目（可多个），获取对应申请单(可多个)
        /// </summary>
        /// <param name="idList">申请项目ID列表</param>
        public async Task<BackJson> GetSelectedApplyFormList(List<Guid> idList)
        {
            var list = await _examApplicationRepository.GetListAsync(x => idList.Contains(x.Id));
            var applyNumberList = list.Select(x => x.ApplyNumber).Distinct().ToList();
            List<ApplyFormDto> applyFormDtos = new List<ApplyFormDto>();
            foreach (var applyNumber in applyNumberList)
            {
                var examApplyItem = await _examApplicationRepository.FirstOrDefaultAsync(x => x.ApplyNumber == applyNumber);
                var applyForm = _mapper.Map<ExamApplication, ApplyFormDto>(examApplyItem!);
                var examItemList = await _examApplicationRepository.GetListAsync(x => x.ApplyNumber == applyNumber);
                var examItemNameList = examItemList.Select(x => x.ExamItemName).ToList();
                var deviceTypeNameList = examItemList.Select(x => x.DeviceType.Name).ToList();
                applyForm.ExamItems = string.Join(",", examItemNameList);
                applyForm.DeviceTypeName = string.Join(",", deviceTypeNameList);
                applyForm.Sex = _commonAppService.GetEnumValueDescription(examApplyItem!.Sex);
                applyFormDtos.Add(applyForm);
            }

            return new BackJson()
            {
                code = 0,
                data = applyFormDtos
            };
        }

        /// <summary>
        /// 获取当前登录科室设备类型
        /// </summary>
        private async Task<List<DeviceType>>? GetCurrentDepartmentDeviceType()
        {
            var depId = _currentUserDep.GetCurrentUserDepartment()!["id"];
            if (depId == null)
            {
                throw new BusinessException(CustomErrorCodes.NotDepartmentUser);
            }
            var dep = await _depparmentRepository.FirstOrDefaultAsync(x => x.Id == Guid.Parse(depId));
            if (dep == null)
            {
                throw new BusinessException(CustomErrorCodes.DepartmentNotExist);
            }

            if (dep.DeviceTypes == null || dep.DeviceTypes.Where(d => d.IsActive).ToList().Count == 0)
            {
                // 提示：科室无可用设备类型
                throw new BusinessException(CustomErrorCodes.NoAnyDeviceTypeInCurrentDepartment);
            }
            return dep.DeviceTypes.Where(d => d.IsActive).ToList();
        }

        /// <summary>
        /// 未预约列表
        /// </summary>
        /// <returns></returns>
        public async Task<BackJson> GetNoAppointmentApplyItemListAsync(string? number, Dictionary<string, object> userData)
        {
            var deviceTypeCodes = (await GetCurrentDepartmentDeviceType()!).Select(x => x.OuterCode).ToList();
            return await _examApplyAppService.GetApplyItemListAsync(number, userData, deviceTypeCodes!, ApplyItemStatus.UnAppointed);
        }

        /// <summary>
        /// 已预约列表
        /// </summary>
        /// <returns></returns>
        public async Task<BackJson> GetAppointedApplyItemListAsync(string? number, Dictionary<string, object> userData)
        {
            var deviceTypeCodes = (await GetCurrentDepartmentDeviceType()!).Select(x => x.OuterCode).ToList();
            return await _examApplyAppService.GetApplyItemListAsync(number, userData, deviceTypeCodes!, ApplyItemStatus.Appointed);
        }

        /// <summary>
        /// 创建号池
        /// </summary>
        public async Task<BackJson> CreatePool(NewPoolDto dto)
        {
            var planPool = await _planPoolRepository.FirstOrDefaultAsync(p => p.Id == dto.PlanPoolId);
            if (planPool == null)
            {
                throw new BusinessException(CustomErrorCodes.PlanPoolNotExist);
            }
            var device = await _deviceRepository.FirstOrDefaultAsync(d => d.Id == dto.DeviceId);
            if (device == null)
            {
                throw new BusinessException(CustomErrorCodes.DeviceNotExist);
            }
            var pool = new Pool();
            var date = dto.Date;
            var start = planPool.TimeInterval.StartTime;
            var end = planPool.TimeInterval.EndTime;
            pool.DeviceId = dto.DeviceId;
            pool.Date = date;
            pool.PlanPoolId = planPool.Id;
            pool.MaxCount = planPool.MaxCount;
            pool.AllowOverFlow = planPool.AllowOverFlow;
            pool.StartTime = new DateTime(date.Year, date.Month, date.Day, start.Hours, start.Minutes, start.Seconds);
            pool.EndTime = new DateTime(date.Year, date.Month, date.Day, end.Hours, end.Minutes, end.Seconds);
            pool.IsActive = true;

            await _poolRepository.InsertAsync(pool);

            return new BackJson()
            {
                code = 0,
                data = _mapper.Map<Pool, PoolDto>(pool)
            };
        }

        /// <summary>
        /// 当前科室下
        /// </summary>
        /// <param name="deviceTypeID"></param>
        /// <returns></returns>
        public async Task<BackJson> GetDeviceList(Guid deviceTypeID)
        {
            var depId = _currentUserDep.GetCurrentUserDepartment()["id"];
            if (string.IsNullOrEmpty(depId))
            {
                throw new BusinessException(CustomErrorCodes.NotDepartmentUser);
            }
            var query = await _deviceRepository.GetQueryableAsync();
            query = query.Where(d => d.DepartmentId == Guid.Parse(depId) && d.DeviceTypeId == deviceTypeID && d.IsActive && d.Status == DeviceStatus.Running && d.ScheduleId != null);
            query = query.OrderByDescending(d => d.Order);
            var list = await _queryableExecuter.ToListAsync(query);
            return new BackJson()
            {
                code = 0,
                data = _mapper.Map<List<Device>, List<DeviceDto>>(list)
            };
        }

        /// <summary>
        /// 获取号池列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<BackJson> GetPoolList(PoolQueryDto dto)
        {
            return await _poolAppService.GetPoolList(dto);
        }

        /// <summary>
        ///  预约提交
        /// </summary>
        /// <param name="examItemList"></param>
        /// <returns></returns>
        public async Task<BackJson> SubmitAppoint(List<SubmitAppointDto> examItemList)
        {
            var poolId = examItemList[0].PoolId;
            var appointInfo = examItemList[0].Appointment;
            var list = await _examApplicationRepository.GetListAsync(e => examItemList.Select(i => i.Id).Contains(e.Id));
            var pool = await _poolRepository.FirstOrDefaultAsync(p => p.Id == poolId);
            if (pool == null)
            {
                throw new BusinessException(CustomErrorCodes.PoolNotExist);
            }

            // 工作量计算
            pool.Current += 1;
            await _poolRepository.UpdateAsync(pool);

            // 创建预约对象
            var appointment = new Appointment();
            appointment.ExamApplications = list;
            appointment.PoolId = poolId;
            appointment.Status = AppointmentStatus.Submit;

            // 生成编号，并确保去重
            string number = string.Empty;
            Appointment? used = null;
            do
            {
                number = _appointmentAppService.GeneratorNumber();
                used = await _appointmentRepository.FirstOrDefaultAsync(a => a.Number.Equals(number));
            }
            while (used != null);
            appointment.Number = number;

            await _appointmentRepository.InsertAsync(appointment);

            for (int i = 0; i < list.Count; i++)
            {
                list[i].PoolId = poolId;
                list[i].AppointmentId = appointment.Id;
                list[i].AppointmentInfo = appointInfo;
            }

            await _examApplicationRepository.UpdateManyAsync(list);

            // 将检查项目预约状态回写给his
            List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
            foreach (var applyItem in list)
            {
                var dto = new ApplyExamItemStatusChangeDto();
                dto.Name = applyItem.Name;
                dto.PatientId = applyItem.PatientId;
                dto.AppointmentInfo = appointment.DisplayName;
                dto.ApplyNumber = applyItem.ApplyNumber;
                dto.ChangeType = ApplyExamItemStatusChangeType.Submit;
                dto.ChangeTypeStr = "新建预约";
                dto.ApplyNumber = appointment.Number;
                dto.ExamItemNumber = applyItem.ExamItemNumber;
                dto.ExamItemCode = applyItem.ExamItemCode;
                dto.ExamItemName = applyItem.ExamItemName;

                changeDtos.Add(dto);
            }
            await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);

            return new BackJson()
            {
                code = 0,
                msg = "预约成功"
            };
        }


        /// <summary>
        /// 提交预约修改
        /// </summary>
        public async Task<BackJson> SubmitEdit(List<SubmitAppointDto> examItemList)
        {

            var list = await _examApplicationRepository.GetListAsync(e => examItemList.Select(i => i.Id).Contains(e.Id));

            #region 旧预约中移除检查项目（如移除后预约中无其他申请项目则删除预约）

            List<Guid> deleteAppointIdList = new List<Guid>();
            foreach (var applyItem in list)
            {
                var oldAppoint = applyItem.Appointment;
                if (oldAppoint != null)
                {
                    oldAppoint.ExamApplications.Remove(applyItem);
                    if (oldAppoint.ExamApplications.Count == 0) // 判断移除检查项目后，预约是否为空
                    {
                        deleteAppointIdList.Add(oldAppoint.Id);
                        if (oldAppoint.Pool.Current >= 1)
                        {
                            oldAppoint.Pool.Current -= 1;
                        }
                    }
                }
            }
            // 删除无检查项目的预约
            if (deleteAppointIdList.Count > 0)
            {
                await _appointmentRepository.DeleteManyAsync(deleteAppointIdList);
            }
            #endregion

            // 新预约的号池
            var poolId = examItemList[0].PoolId;
            var appointInfo = examItemList[0].Appointment;
            var pool = await _poolRepository.FirstOrDefaultAsync(p => p.Id == poolId);
            if (pool == null)
            {
                throw new BusinessException(CustomErrorCodes.PoolNotExist);
            }

            // 计算新工作量
            pool.Current += 1;
            await _poolRepository.UpdateAsync(pool);

            // 创建新的预约
            var appointment = new Appointment();
            appointment.ExamApplications = list;
            appointment.PoolId = poolId;
            appointment.Status = AppointmentStatus.Submit;

            // 生成编号，并确保去重
            string number = string.Empty;
            Appointment? used = null;
            do
            {
                number = _appointmentAppService.GeneratorNumber();
                used = await _appointmentRepository.FirstOrDefaultAsync(a => a.Number.Equals(number));
            }
            while (used != null);
            appointment.Number = number;
            await _appointmentRepository.InsertAsync(appointment);

            for (int i = 0; i < list.Count; i++)
            {
                list[i].PoolId = poolId;
                list[i].AppointmentId = appointment.Id;
                list[i].AppointmentInfo = appointInfo;
            }

            await _examApplicationRepository.UpdateManyAsync(list);

            // 修改预约后回写his
            List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
            foreach (var applyItem in list)
            {
                var dto = new ApplyExamItemStatusChangeDto();
                dto.Name = applyItem.Name;
                dto.PatientId = applyItem.PatientId;
                dto.AppointmentInfo = applyItem.AppointmentInfo;
                dto.ChangeType = ApplyExamItemStatusChangeType.Edit;
                dto.ChangeTypeStr = "修改预约";
                dto.AppointmentNo = appointment.Number;
                dto.ApplyNumber = applyItem.ApplyNumber;
                dto.ExamItemNumber = applyItem.ExamItemNumber;
                dto.ExamItemCode = applyItem.ExamItemCode;
                dto.ExamItemName = applyItem.ExamItemName;
                changeDtos.Add(dto);
            }
            await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);

            return new BackJson
            {
                code = 0,
                msg = "修改成功"
            };
        }

        /// <summary>
        /// 取消预约
        /// </summary>
        /// <param name="examItemList"></param>
        /// <returns></returns>
        public async Task<BackJson> Cancel(List<SubmitAppointDto> examItemList)
        {
            var idList = examItemList.Select(e => e.Id).ToList();
            var list = await _examApplicationRepository.GetListAsync(e => idList.Contains(e.Id));

            // 预约中移除检查项目(如移除后预约中无检查项目，则删除预约)
            List<Appointment> deleteAppointList = new List<Appointment>();
            foreach (var examItem in list)
            {
                var appointment = examItem.Appointment;
                if (appointment != null)
                {
                    appointment.ExamApplications.Remove(examItem);
                    if (appointment.ExamApplications.Count == 0)
                    {
                        deleteAppointList.Add(appointment);
                        if (appointment.Pool.Current >= 1)
                        {
                            appointment.Pool.Current -= 1;
                        }
                    }
                }
                examItem.Appointment = null;
                examItem.PoolId = null;
                await _examApplicationRepository.UpdateAsync(examItem);
            }
            await _appointmentRepository.DeleteManyAsync(deleteAppointList);

            List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
            foreach (var applyItem in list)
            {
                var dto = new ApplyExamItemStatusChangeDto();
                dto.Name = applyItem.Name;
                dto.PatientId = applyItem.PatientId;
                dto.AppointmentInfo = null;
                dto.ChangeType = ApplyExamItemStatusChangeType.Cancel;
                dto.ChangeTypeStr = "取消预约";
                dto.AppointmentNo = null;
                dto.ApplyNumber = applyItem.ApplyNumber;
                dto.ExamItemNumber = applyItem.ExamItemNumber;
                dto.ExamItemCode = applyItem.ExamItemCode;
                dto.ExamItemName = applyItem.ExamItemName;

                changeDtos.Add(dto);
            }
            await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);

            return new BackJson
            {
                code = 0,
                msg = "取消成功"
            };
        }
    }
}
